package com.jichangxiu.common.utils;

import cn.hutool.json.JSONUtil;
import com.jichangxiu.common.constant.Constant;
import com.jichangxiu.common.entity.bo.Payload;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;

import java.math.BigDecimal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * JwtUtils:
 *
 * @author Jcx
 * @create 2023-04-03 21:49
 */
public class JwtUtils {

    /**
     * 创建短令牌
     *
     * @return 短令牌
     */
    public static String createJti() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 私钥加密
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间，单位分钟
     * @return JWT
     */
    public static String createJwtExpireInMinutes(Object userInfo, PrivateKey privateKey, Long expire) {
        return createJwtExpireInSeconds(userInfo, privateKey, expire * 60);
    }

    /**
     * 私钥加密
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间，单位秒
     * @return JWT
     */
    public static String createJwtExpireInSeconds(Object userInfo, PrivateKey privateKey, Long expire) {
        return createJwtExpireInSeconds(userInfo, createJti(), privateKey, expire);
    }

    /**
     * 私钥加密
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间，单位秒
     * @return JWT
     */
    public static String createJwtExpireInSeconds(Object userInfo, String jti, PrivateKey privateKey, Long expire) {
        return Jwts.builder().claim(Constant.PAYLOAD_KEY, JsonUtils.toString(userInfo)).setId(jti).setExpiration(DateTime.now().plusSeconds(new BigDecimal(expire).intValue()).toDate()).signWith(privateKey, SignatureAlgorithm.RS256).compact();
    }

    /**
     * 公钥解析 jwt
     *
     * @param jwt       用户请求中的 jwt
     * @param publicKey 公钥
     * @return Jws<Claims>
     */
    private static Jws<Claims> parserJwt(String jwt, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(jwt);
    }

    /**
     * 获取 jwt 中的用户信息
     *
     * @param jwt       用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromJwt(String jwt, PublicKey publicKey, Class<T> userType) {
        Jws<Claims> claimsJws = parserJwt(jwt, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setJti(body.getId());
        String jsonStr = body.get(Constant.PAYLOAD_KEY).toString();
        if (JSONUtil.isTypeJSON(jsonStr)) {
            claims.setUserInfo(JSONUtil.toBean(jsonStr, userType));
        } else {
            claims.setUserInfo((T) body.get(Constant.PAYLOAD_KEY));
        }
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 获取 jwt 中的载荷信息
     *
     * @param jwt       用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromJwt(String jwt, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserJwt(jwt, publicKey);
        Claims body = claimsJws.getBody();
        Payload<T> claims = new Payload<>();
        claims.setJti(body.getId());
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 判断 jwt 是否过期
     *
     * @param expiration payload 时长
     * @return 是否过期
     */
    public static boolean jwtIsExpired(Date expiration) {
        return expiration.before(new Date());
    }

}
